/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

namespace Engine
{
	/*****************************************************************************************************/
	template<class T,class P>
	ThreadState<T,P>::ThreadState()
	{
		// Should be consistent with code in Reset.
		Clear();
	}
	/*****************************************************************************************************/
	template<class T,class P>
	void ThreadState<T,P>::Clear()
	{
		m_dwCycleTime = 0;
		m_bStopThread = false;
		m_eState = UNINITIALIZED;		
		m_pObject = 0;
	}
	/*****************************************************************************************************/
	template<class T,class P>
	ThreadState<T,P>::~ThreadState()
	{		
	}
	/*****************************************************************************************************/
	template<class T,class P>
	void ThreadState<T,P>::Reset()
	{
		CloseThreadHandle();

		// This code should be consistent with
		// code in the constructor.

		m_RunEvent.Reset();
		m_WorkEvent.Reset();

		Clear();
	}
	/*****************************************************************************************************/
	template<class T,class P>
	void ThreadState<T,P>::CloseThreadHandle()
	{
		m_Thread.Close();
	}
	/*****************************************************************************************************/
	/*                                      THREAD                                                       */
	/*****************************************************************************************************/
	template<class T,class P>
	Thread<T,P>::Thread(P param, unsigned long dwMilliSecs,unsigned long dwStackSize)
	{		
		m_td.m_param = param;
		m_td.m_Thread.Create(dwStackSize, &ThreadImpl, &m_td);
		m_td.m_dwCycleTime = dwMilliSecs;
		m_td.m_eState = ThreadState<T,P>::INITIALIZED;
		m_td.m_pObject = this;
	}
	/*****************************************************************************************************/
	template<class T,class P>
	Thread<T,P>::~Thread()
	{
		// Do what we can to end thread,
		// but ThreadableTask must still cooperate!

		if ( m_td.m_Thread.IsCreated() )
		{
			Run();
			DoWork();

			RequestStop();
			WaitUntilDone();
		}
	}
	/*****************************************************************************************************/
	template<class T,class P>
	void Thread<T,P>::Run()const
	{
		Lock lock(this);
		if ( m_td.m_eState == ThreadState<T,P>::INITIALIZED )
		{
			m_td.m_RunEvent.Signal();
		}
	}
	/*****************************************************************************************************/
	template<class T,class P>
	void Thread<T,P>::RequestStop()const
	{
		Lock lock(this);
		m_td.m_bStopThread = true;
		Run();
		DoWork();		
	}
	/*****************************************************************************************************/
	template<class T,class P>
	bool Thread<T,P>::IsStopping() const
	{
		Lock lock(this);
		return m_td.m_bStopThread == 1;
	}
	/*****************************************************************************************************/
	template<class T,class P>
	void Thread<T,P>::DoWork()const
	{
		Lock lock(this);
		if ( m_td.m_eState == ThreadState<T,P>::SLEEPING )
		{
			m_td.m_WorkEvent.Signal();
		}
	}
	/*****************************************************************************************************/
	template<class T,class P>
	unsigned long Thread<T,P>::GetCycleTime() const
	{
		Lock lock(this);
		return m_td.m_dwCycleTime;
	}
	/*****************************************************************************************************/
	template<class T,class P>
	void Thread<T,P>::SetCycleTime( unsigned long dwCycleTime )
	{
		Lock lock(this);
		m_td.m_dwCycleTime = dwCycleTime;
	}
	/*****************************************************************************************************/
	template<class T,class P>
	unsigned long Thread<T,P>::GetExitCode()const
	{
		Lock lock(this);
		return m_td.m_hThread.GetExitCode();
	}
	/*****************************************************************************************************/
	template<class T,class P>
	void Thread<T,P>::SetPriority(THREAD_PRIORITY priority)
	{
		Lock lock(this);
		m_td.m_hThread.SetPriority( priority );
	}
	/*****************************************************************************************************/
	template<class T,class P>
	void Thread<T,P>::WaitUntilDone()const
	{
		m_td.m_Thread.WaitUntilDone();
	}
	/*****************************************************************************************************/
	template<class T,class P>
	int Thread<T,P>::GetThreadState() const
	{
		Lock lock(this);
		return m_td.m_eState;
	}
	/*****************************************************************************************************/
	template<class T,class P>
	unsigned long Thread<T,P>::ThreadImpl(void* lpParam)
	{
		ThreadState<T,P>* ptrState = (ThreadState<T,P>*) lpParam;

		// Synchronize on the Run Event
		ptrState->m_RunEvent.Wait(INFINITE);

		long bStopThread = false;
		bool bWorkInProgress = true;

		unsigned long dwCycleTime = 0;
		unsigned long dwReturn = 0;
		Thread *pObj = 0;

		{
			RefPtr<T,WeakPtrExtension> pWorkObj;
			// Latch values once to start loop
			{		
				pObj = ptrState->m_pObject;
				Lock lock(pObj);

				try
				{
					pWorkObj = RefPtr<T,WeakPtrExtension>(new T(ptrState->m_param));
					pObj->m_thObj = pWorkObj;
				}
				catch(std::exception& e)
				{
#ifdef WIN32
					MessageBox(NULL,e.what(),"Exception",MB_ICONERROR|MB_OK);
#else
					std::cout<<"Exception: "<<e.what();
#endif
					bWorkInProgress = false;
				}
				catch(...)
				{
#ifdef WIN32
					MessageBox(NULL,"Unknown exception","Exception",MB_ICONERROR|MB_OK);
#else
					std::cout<<"Exception: "<<e.what();
#endif
					bWorkInProgress = false;
				}
				
				dwCycleTime = ptrState->m_dwCycleTime;
				ptrState->m_eState = ThreadState<T,P>::SLEEPING;
			}

			while ( !bStopThread && bWorkInProgress )
			{
				// Wait for a Work event, or a timeout.
				pObj->WaitWorkSignal(pObj);
				ptrState->m_WorkEvent.Wait(dwCycleTime);
				AtomicExchange(&ptrState->m_eState,ThreadState<T,P>::WORKING);
				
				try
				{
					bWorkInProgress = pWorkObj->Task( &dwReturn );
				}
				catch(std::exception& e)
				{
#ifdef WIN32
					MessageBox(NULL,e.what(),"Exception",MB_ICONERROR|MB_OK);
#else
					std::cout<<"Exception: "<<e.what();
#endif
					bWorkInProgress = false;
				}
				catch(...)
				{
					bWorkInProgress = false;
				}


				if ( bWorkInProgress )
				{

					dwCycleTime = AtomicExchange(&ptrState->m_dwCycleTime, 0);
					AtomicExchange(&ptrState->m_dwCycleTime,dwCycleTime);

					bStopThread = AtomicExchange(&ptrState->m_bStopThread, 0);
					AtomicExchange(&ptrState->m_bStopThread, bStopThread);

					AtomicExchange(&ptrState->m_eState, ThreadState<T,P>::SLEEPING);
					ptrState->m_WorkEvent.Reset();					
				}
			}
		}

		// Thread finished.
		{
			Lock lock(pObj);
			ptrState->m_bStopThread = true;
			ptrState->m_eState = ThreadState<T,P>::DONE;
		}
		
		return dwReturn;
	}
	/*****************************************************************************************************/
}
